package gazzera.node.core.plugins.shareplugin;

import gazzera.gui.*;
import gazzera.gui.messages.*;
import gazzera.node.core.*;
import gazzera.utils.*;

import java.io.*;
import java.security.*;
import java.sql.*;
import java.util.*;


import com.sleepycat.persist.*;

@Deprecated
public class FolderScanner extends AbstractTask implements Task
{
	private FileDb db;
	private GuiInterface gui;
	private long lastSent = 0;
	private ShareList shares;
	private byte[] buf = new byte[1024*20];
	
	public FolderScanner(FileDb db, GuiInterface gui, ShareList shares)
	{
		this.db = db;
		this.gui = gui;
		this.shares = new ShareList(shares); // copy becouse this will run in a separated thread
	}
	
	public Object run(GuiInterface gui)
	{
		doScanFolders();
		doHashFiles();
		return null;
		
	}

	private void doHashFiles()
	{
		try
		{
			hashAll();
		}
		catch (InterruptedException e)
		{
			System.out.println("File Hasher interrupted");
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		db.flush();
	}

	

	private void doScanFolders()
	{
		try
		{
			long startTime = System.currentTimeMillis();
			int parsed = scanFolders();			
			long time = System.currentTimeMillis()-startTime;
			this.gui.pass(new Log("Parsed: "+parsed+". Time: "+(time / 1000)+ " seconds."));
			
			checkInterrupted();
			
		}
		catch (InterruptedException e)
		{
		    gui.pass(new Log("Folder scanner was interrupted"));
		}
		catch (Exception e)
		{
			
			e.printStackTrace();
		}
	}
	 private boolean different(FileDesc x, File f)
	    {
	    	if (!f.exists())
			{
	    		// removed!
				return true;
			}
	    	if(x.directory != f.isDirectory())
	    	{
	    		return true;
	    	}
	    	if(x.timestamp != f.lastModified())
	    	{
	    		return true;
	    	}
	    	if(x.size != f.length())
	    	{
	    		return true;
	    	}
	    	return false;
	    }
	    
	private int parseX(FileDesc f, File real, int shareId) throws InterruptedException
	{
		checkInterrupted();
		
		if((System.currentTimeMillis()-lastSent) > 1000)
		{
		    gui.pass(new Log("Parsing "+real));
		    lastSent = System.currentTimeMillis();
		}
		
		
		File[] childs = real.listFiles();
		Set<File> realChildrenToAdd;
		if (childs!=null) {
			realChildrenToAdd = new HashSet<File>(Arrays.asList(childs)); 
		}
		else
		{
			realChildrenToAdd = new HashSet<File>();
		}
    	Map<File, FileDesc> folderToRecurse =  new HashMap<File, FileDesc>();
    	int numItems = realChildrenToAdd.size();
    	
    	// now cicle the children on our filedesc
    	EntityCursor<FileDesc> c = db.children(f.id);
    	try
    	{
    		for (FileDesc child : c)
			{
    			File realChild =  new File(real, child.filename);
				if(different(child,realChild))
				{
					// file was recorded but has changed/removed.
					// remove from the db
					db.delete(child.id);
				}
				else
				{
					// file is still there!
					// remove from the "todo" list
					realChildrenToAdd.remove(realChild);
					// if it's a directory, recurse! (later)
					if (realChild.isDirectory())
					{
						folderToRecurse.put(realChild, child);
					}
					
				}
			}
    	}
    	finally
    	{
    		c.close();
    	}
    	
    	// now, let's save new stuff!
    	for (File realFile : realChildrenToAdd)
		{
    		checkInterrupted();
    		
    		FileDesc d = new FileDesc();
	    	d.directory = realFile.isDirectory();
	    	// assume it has subdir until proven wrong, as they usually have
	    	// so later we have less probability to update the entry.
	    	d.hasSubdir = true; 
	    	d.filename = realFile.getName();
	    	d.id = db.nextKey();
	    	d.parentId = f.id;
	    	d.size = realFile.length();
	    	d.timestamp = realFile.lastModified();
	    	d.shareId = shareId;
	    	d.location = realFile.getAbsolutePath();
	    	db.put(d);
	    	
	    	// if it's a brand new directory, let's recurse!
	    	if (d.directory)
			{
				folderToRecurse.put(realFile, d);
			}
		}
    	
    	// now we know if this folder has subfolders or not
    	if (folderToRecurse.size()==0)
		{
			f.hasSubdir = false;
			db.put(f);
		}
    	
    	// now, let's recurse!
    	for (File realDir : folderToRecurse.keySet())
		{
			FileDesc dir = folderToRecurse.get(realDir);
			numItems += parseX(dir, realDir, shareId);
		}
    	
    	
    	return numItems;
    }
    
    
	
	public int parseShare(File dir, int shareId) throws InterruptedException
    {
    	if(!(dir.exists()&& dir.isDirectory())) return 0;
    	
    	// find the id, if it exists
    	FileDesc f = db.findId(0, dir.getName());
    	if (f == null)
		{
    		// save if not exists
    		f = new FileDesc();
    		f.id = db.nextKey();
		}
		f.directory = dir.isDirectory();
    	f.filename = dir.getName();
    	f.parentId = 0;
    	f.size = dir.length();
    	f.timestamp = dir.lastModified();
    	f.shareId = shareId;
    	f.hasSubdir = true;
    	f.location = dir.getAbsolutePath();
    	db.put(f);
    	
    	return parseX(f, dir, shareId);
    }
    
    
	
	private int scanFolders() throws SQLException, InterruptedException
	{
		int num = 0;
		for (Integer id : shares.keySet())
		{
			Share share = shares.get(id);
			num += parseShare(share.getFolder(), id);
		}
		return num;
		
	    /*
	    if(Platform.isWindows())
	    {
		
		int num = 0;
		num += parseShare(new File("c:\\mame"), 1);
		//num += parseShare(new File("d:\\nicola\\erlang"), 2);
		//num += parseShare(new File("d:\\nicola\\apache-log4j-1.2.15"), 3);
		num += parseShare(new File("c:\\lazarus"), 4);
		// num += parseShare(new File("C:\\mysql-5.0.51a-win32"), 5);
		num += parseShare(new File("D:\\doc"), 6);
		//num += parseShare(new File("/home/nicola/Go-SDL"), 5);
		// num += parseShare(new File("C:\\Programmi"), 6);
		//num += parseShare(new File("C:\\dev"), 7);
		//num += parseShare(new File("d:\\nicola"), 7);
		
		return num;
	    }
	    else
	    {
		int num = 0;
		num += parseShare(new File("/storage/Media/Ebook"), 1);
		
		return num;
	    }*/
	}
	
	
	private Hash hashFile(File f) throws Exception
	{
		MessageDigest m = MessageDigest.getInstance("SHA1");
		FileInputStream fis = new FileInputStream(f);
		try
		{
			int r = fis.read(buf);
			while (r>=0)
			{
				m.update(buf, 0, r);
				r = fis.read(buf);
				checkInterrupted();
			}
			return new Hash(m.digest());
		}
		finally
		{
			fis.close();
		}
	}
	
	private void hashAll() throws Exception
	{
		EntityCursor<FileDesc> c = db.filesByHash(Hash.EMPTY);
		try
		{
			for (FileDesc fd : c)
			{
				if ((fd.id != 0) && (!fd.directory))
				{
					File f = new File(fd.location);
					
					boolean ok = shares.isShared(f);
					if(ok)
					{
						// Log.log("Hashing: "+f);
						gui.pass(new Log("Hashing file " + f.getName()));
						try
						{
						    Hash hash = hashFile(f);
						    gui.pass(new Log("Hash " + f.getName() + " " + f.length() + " " + hash));
						    db.setHash(fd.id, hash);
						}
						catch (Exception e)
						{
						    gui.pass(new Log("Unable to Hash " + f.getName() + ": " +e.getMessage()));
						}
					}
				}
			}
		}
		finally
		{
			c.close();
		}

	}


	
}
